package com.example.sanguogameserver.service;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson2.JSON;
import com.baomidou.mybatisplus.extension.service.IService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.example.sanguogameserver.consts.RedisKeyConst;
import com.example.sanguogameserver.dto.Message;
import com.example.sanguogameserver.dto.MessageDTO;
import com.example.sanguogameserver.dto.battle.BattleDetail;
import com.example.sanguogameserver.entity.Chat;
import com.example.sanguogameserver.entity.Player;
import com.example.sanguogameserver.enums.ChatTypeEnum;
import com.example.sanguogameserver.enums.EnableEnum;
import com.example.sanguogameserver.enums.OnlineEnum;
import com.example.sanguogameserver.logic.BattleLogic;
import com.example.sanguogameserver.mapper.ChatMapper;
import com.example.sanguogameserver.request.ChatSendMessageReq;
import com.example.sanguogameserver.response.GetMessageResp;
import com.example.sanguogameserver.response.MessageResp;
import com.example.sanguogameserver.util.AuthUtil;
import com.example.sanguogameserver.util.ChatUtil;
import com.example.sanguogameserver.util.RedisUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * <p>
 * 服务实现类
 * </p>
 *
 * @author baomidou
 * @since 2024-03-25
 */
@Service
public class ChatService extends ServiceImpl<ChatMapper, Chat> implements IService<Chat> {

    @Autowired
    private PlayerService playerService;

    @Autowired
    private RedisUtil redisUtil;
    @Autowired
    private AsyncMessageService asyncMessageService;

    @Autowired
    private BattleLogic battleLogic;

    public void sendMessage(ChatSendMessageReq chatSendMessageReq) {
        Integer sendPlayerId = AuthUtil.getCurrPlayerId();

        if (chatSendMessageReq.getChatType() == ChatTypeEnum.privateChat)
            sendPrivateMessage(chatSendMessageReq, sendPlayerId);

        if (chatSendMessageReq.getChatType() == ChatTypeEnum.world)
            worldSendMessage(sendPlayerId, chatSendMessageReq);

    }

    private void worldSendMessage(Integer sendPlayerId, ChatSendMessageReq chatSendMessageReq) {

        Player sendPlayer = playerService.getById(sendPlayerId);
        MessageDTO messageDTO = redisUtil.getObj(RedisKeyConst.ChatWORLD.name() + sendPlayer.getServerId(), MessageDTO.class);
        Message message = new Message();
        message.setMessageId(IdUtil.simpleUUID());
        message.setSendPlayerId(sendPlayerId);
        message.setMessage(chatSendMessageReq.getMessage());
        message.setSendDate(chatSendMessageReq.getSendDate());
        message.setSendHead(sendPlayer.getHead());
        message.setSendName(sendPlayer.getPlayName());
        message.setChatType(chatSendMessageReq.getChatType());
        message.setGroupId(chatSendMessageReq.getGroupId());
        messageDTO.getMessages().add(message);
        if (messageDTO.getMessages().size() > 500) {
            messageDTO.setMessages(messageDTO.getMessages().stream().skip(messageDTO.getMessages().size() - 500).toList());
        }
        redisUtil.setObject(RedisKeyConst.ChatWORLD.name() + sendPlayer.getServerId(), messageDTO);
    }

    private void sendPrivateMessage(ChatSendMessageReq chatSendMessageReq, Integer sendPlayerId) {
        Player player = playerService.getById(chatSendMessageReq.getPlayerId());

        Player sendPlayer = playerService.getById(sendPlayerId);
        if (player.getOnline() == OnlineEnum.Offline) {
            //离线消息,放到数据库,供用户读取
            Chat chat = new Chat();
            chat.setMessageId(IdUtil.simpleUUID());
            chat.setPlayerAId(sendPlayerId);
            chat.setPlayerBId(player.getId());
            chat.setMessage(chatSendMessageReq.getMessage());
            chat.setSenderId(sendPlayerId);
            chat.setCreateDate(chatSendMessageReq.getSendDate());
            chat.setSendHead(sendPlayer.getHead());
            chat.setSendName(sendPlayer.getPlayName());
            chat.setChatType(chatSendMessageReq.getChatType());
            chat.setGroupId(chatSendMessageReq.getGroupId());
            // 未读取
            chat.setReadStatus(EnableEnum.False);
            save(chat);
            return;
        }
        // 在线,放到redis供用户拉取
        MessageDTO messageDTO = redisUtil.getHash(RedisKeyConst.Chat.name() + player.getId(), sendPlayerId, MessageDTO.class);
        if (messageDTO == null) messageDTO = new MessageDTO();
        Message message = new Message();
        message.setMessageId(IdUtil.simpleUUID());
        message.setSendPlayerId(sendPlayerId);
        message.setMessage(chatSendMessageReq.getMessage());
        message.setSendDate(chatSendMessageReq.getSendDate());
        message.setSendHead(sendPlayer.getHead());
        message.setSendName(sendPlayer.getPlayName());
        message.setChatType(chatSendMessageReq.getChatType());
        message.setGroupId(chatSendMessageReq.getGroupId());
        messageDTO.getMessages().add(message);

        redisUtil.putHash(RedisKeyConst.Chat.name() + player.getId(), sendPlayerId, messageDTO, RedisKeyConst.Chat.getDuration());
    }

    public GetMessageResp getMessage(String worldMessageId, Integer attackTaskId) {
        Integer receivedPlayerId = AuthUtil.getCurrPlayerId();
        Map<Object, Object> entrieMap = redisUtil.entries(RedisKeyConst.Chat.name() + receivedPlayerId);
        List<MessageResp> messageResps = new ArrayList<>();
        for (Map.Entry<Object, Object> entrie : entrieMap.entrySet()) {
            MessageResp messageResp = new MessageResp();
            messageResp.setFriendPlayerId(Integer.parseInt(entrie.getKey().toString()));
            MessageDTO messageDTO = JSON.parseObject(entrie.getValue().toString(), MessageDTO.class);
            if (CollUtil.isEmpty(messageDTO.getMessages())) continue;
            messageResp.setMessageDTO(messageDTO);

            messageResps.add(messageResp);
        }
        MessageDTO wolrdMessageDTO = new MessageDTO();
        // 世界消息,跳过去已经加载过的消息
        if (redisUtil.hasKey(RedisKeyConst.ChatWORLD.name() + AuthUtil.getCurrServerId())) {
            wolrdMessageDTO = redisUtil.getObj(RedisKeyConst.ChatWORLD.name() + AuthUtil.getCurrServerId(), MessageDTO.class);
            List<Message> messages = wolrdMessageDTO.getMessages();

            if (StrUtil.isNotEmpty(worldMessageId)) {
                List<Message> newMessage = new ArrayList<>();
                boolean isNew = false;
                for (Message message : messages) {
                    if (message.getMessageId().equals(worldMessageId)) {
                        isNew = true;
                    }
                    if (isNew) {
                        newMessage.add(message);
                    }
                }
                if (CollUtil.isNotEmpty(newMessage)) {
                    wolrdMessageDTO.setMessages(newMessage);
                }
            }
        }


        GetMessageResp getMessageResp = new GetMessageResp();
        getMessageResp.setMessageResps(messageResps);
        getMessageResp.setWorldMessageDTO(wolrdMessageDTO);

        // 战斗消息
        if (attackTaskId != null && attackTaskId != 0) {
            BattleDetail battleDetail = battleLogic.getBattleDetail(attackTaskId);
            getMessageResp.setBattleDetail(battleDetail);
        }

        // 异步处理已读消息
        asyncMessageService.processMessageRead(receivedPlayerId, messageResps);
        return getMessageResp;
    }

    /**
     * 过期
     *
     * @param key
     */
    @Transactional
    public void expirationKey(String key) {
        // 判断玩家是否还在线
        String playerStr = key.replace(RedisKeyConst.Chat.name(), "");
        Integer playerId = Integer.parseInt(playerStr);
        boolean isOnline = redisUtil.hasKey(RedisKeyConst.Online.name() + playerStr);
        if (isOnline) {
            // 在线
            // 在线不放进数据库,继续放在redis里 把过期时间重新设置
            redisUtil.setDeleteHashDuration(key, RedisKeyConst.Chat.getDuration());
        } else {
            // 离线
            // 离线消息丢到数据库
            // 获取所有消息
            Map<Object, Object> entries = redisUtil.entries(key);
            List<Chat> chatAll = new ArrayList<>();

            for (Map.Entry<Object, Object> entry : entries.entrySet()) {
                int friendPlayerId = Integer.parseInt(entry.getKey().toString());
                MessageDTO messageDTO = JSON.parseObject(entry.getValue().toString(), MessageDTO.class);
                // 先从redis里删除这条key
                redisUtil.deleteHash(key, friendPlayerId);
                if (CollUtil.isNotEmpty(messageDTO.getMessages())) {
                    List<Chat> chats = messageDTO.getMessages().stream().map(item -> ChatUtil.conversionChar(item, playerId)).toList();
                    chatAll.addAll(chats);
                }
            }

            if (CollUtil.isNotEmpty(chatAll)) {
                saveBatch(chatAll);
            }

        }

    }

    @Transactional
    public void dbToRedis(Integer playerId) {
        List<Chat> chats = baseMapper.selectByReceivePlayerId(playerId);
        Map<Integer, List<Chat>> readMessageMap = chats.stream().collect(Collectors.groupingBy(Chat::getSenderId));
        for (Map.Entry<Integer, List<Chat>> entry : readMessageMap.entrySet()) {
            MessageDTO messageDTO = new MessageDTO();
            messageDTO.getMessages().addAll(entry.getValue().stream().map(ChatUtil::conversionMessage).toList());
            redisUtil.putHash(RedisKeyConst.Chat.name() + playerId, entry.getKey(), messageDTO, RedisKeyConst.Chat.getDuration());
        }
        // 放完之后删除
        removeBatchByIds(chats);
    }

}
